package contegor.scire.core.queries;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;

import contegor.scire.core.Connectable;
import contegor.scire.core.Container;
import contegor.scire.core.Id;
import contegor.scire.core.InfiniteSingletonList;
import contegor.scire.core.ReturnPoint;
import contegor.scire.core.ReturnPointAdapter;
import contegor.scire.core.Task;
import contegor.scire.core.TaskId;

/**
 * {@link Query} that filters the result of other queries. This filtering will be lead by a set of {@link WeightCondition}s
 * and will return only those results that have the higher {@link WeightCondition#weight(contegor.scire.core.Connectable)} value. 
 *
 * @version $Revision$
 * @author  Héctor Cirbián Pousa ($Author$)
 */
public class WeightFilter<I extends Connectable> extends QueryWithDependencies<I,I> {
  private static final long serialVersionUID = 8882114129581448112L;

  private WeightCondition<I> condition;
  private ConcurrentNavigableMap<Integer, List<I>> results;

  public WeightFilter(WeightCondition<I> condition, Query<I> dependencie) {
    super(dependencie);
    this.condition = condition;
    this.results   = new ConcurrentSkipListMap<Integer, List<I>>();
  }

  public WeightFilter(Id targetContainerId, WeightCondition<I> condition, Query<I> ... dependencies) {
    super(targetContainerId, dependencies);
    this.condition = condition;
    this.results   = new ConcurrentSkipListMap<Integer, List<I>>();
  }

  public void process(Container container) {
    Map.Entry<Integer, List<I>> last = results.lastEntry();
    if (last != null)
      this.returnPoint.manage(this.taskId, last.getValue());
  }

  @Override
  public List<? extends Task<I>> getDependencies() {
    return dependencies;
  }

  @Override
  public List<? extends ReturnPoint<I>> getDependenciesReturnPoints() {
    if (this.dependencies == null)
      return Collections.emptyList();

    ReturnPoint<I> dependenciesReturnPoint = new ReturnPointAdapter<I>() {
      
      public <Y extends I> void manage(TaskId taskId, Y result) {
        int weight = WeightFilter.this.condition.weight(result);
        List<I> sameWeightResults = WeightFilter.this.results.get(weight);
        if (sameWeightResults == null) {
          sameWeightResults = Collections.synchronizedList(new LinkedList<I>());
          List<I> aux = WeightFilter.this.results.putIfAbsent(weight, sameWeightResults);
          if (aux != null)
            sameWeightResults = aux;
        }
        sameWeightResults.add(result);
      }
      
      public void manage(TaskId taskId, Exception exception) {
        returnPoint.manage(taskId, exception);
      }
    };

    return new InfiniteSingletonList<ReturnPoint<I>>(dependenciesReturnPoint, dependencies.size());
  }
}